﻿using Common.Infrastructure.Structs;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;

namespace Common.Infrastructure.Utils
{
    public class WebApiUtils
    {
        private static HttpClientHandler httpClientHandler = null;

        private static HttpClient httpClientInstance = new HttpClient(HttpClientHandler) { Timeout = TimeSpan.FromMinutes(10) };

        private static readonly object LockPad = new object();

        private static DateTime clientExpired = DateTime.Now.AddMinutes(15);

        public static HttpClientHandler HttpClientHandler
        {
            get
            {
                if (httpClientHandler == null)
                {
                    httpClientHandler = new HttpClientHandler();
                    httpClientHandler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
                }

                return httpClientHandler;
            }
        }

        /// <summary>
        /// Get异步请求.
        /// </summary>
        /// <param name="request">请求对象.</param>
        /// <returns>响应对象.</returns>
        public static async Task<WebApiResponse> GetAsync(WebApiGetRequest request)
        {
            var webClient = GetHttpClient();

            return await GetByClientAsync(webClient, request);
        }

        /// <summary>
        /// Post异步请求.
        /// </summary>
        /// <param name="request">请求对象.</param>
        /// <returns>响应对象.</returns>
        public static async Task<WebApiResponse> PostAsync(WebApiPostRequest request)
        {
            var webClient = GetHttpClient();

            return await PostByClientAsync(webClient, request);
        }

        /// <summary>
        /// 根据Http客户端Get异步请求.
        /// </summary>
        /// <param name="webClient">http客户端.</param>
        /// <param name="request">请求对象.</param>
        /// <returns>响应对象.</returns>
        public static async Task<WebApiResponse> GetByClientAsync(HttpClient webClient, WebApiGetRequest request)
        {
            WebApiResponse response = new WebApiResponse();
            string url = request.Url;
            string p = GetUrlParam(request.Params);
            if (!string.IsNullOrEmpty(p))
            {
                url = url + "?" + p;
            }

            var requestMessage = new HttpRequestMessage(HttpMethod.Get, url);

            if (request.HeadParams != null && request.HeadParams.Any())
            {
                request.HeadParams.ForEach(t => requestMessage.Headers.Add(t.Key, t.Value));
            }

            var ret = await webClient.SendAsync(requestMessage);

            ret.EnsureSuccessStatusCode();
            var json = await ret.Content.ReadAsStringAsync();
            response.Data = json;
            return response;
        }

        /// <summary>
        /// 根据Http客户端Post异步请求.
        /// </summary>
        /// <param name="webClient">http客户端.</param>
        /// <param name="request">请求对象.</param>
        /// <returns>响应对象.</returns>
        public static async Task<WebApiResponse> PostByClientAsync(HttpClient webClient, WebApiPostRequest request)
        {
            var requestMessage = new HttpRequestMessage(HttpMethod.Post, request.Url);

            WebApiResponse response = new WebApiResponse();
            StringContent content;
            if (request.Params != null && request.Params.Any())
            {
                content = new StringContent(GetUrlParam(request.Params), Encoding.UTF8, "application/json");
            }
            else if (request.Obj != null)
            {
                content = new StringContent(request.Obj?.ToJson(), Encoding.UTF8, "application/json");
            }
            else if (!string.IsNullOrEmpty(request.ObjStr))
            {
                content = new StringContent(request.ObjStr, Encoding.UTF8, "application/json");
            }
            else
            {
                var obj = new { key = string.Empty };
                content = new StringContent(obj.ToJson(), Encoding.UTF8, "application/json");
            }

            content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
            if (request.HeadParams != null && request.HeadParams.Any())
            {
                string token = string.Empty;
                request.HeadParams.ForEach(t =>
                {
                    if (string.Compare(t.Key, "Authorization", true) == 0)
                    {
                        token = t.Value;
                        return;
                    }

                    content.Headers.Add(t.Key, t.Value);
                });

                if (!string.IsNullOrEmpty(token))
                {
                    requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
                }
            }

            requestMessage.Content = content;

            var ret = await webClient.SendAsync(requestMessage);

            ret.EnsureSuccessStatusCode();
            var json = await ret.Content.ReadAsStringAsync();
            response.Data = json;
            return response;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="url"></param>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static async Task<T> PostAsync<T>(string url, object obj)
        {
            var webClient = GetHttpClient();
            return await PostByClientAsync<T>(webClient, url, obj);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="webClient"></param>
        /// <param name="url"></param>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static async Task<T> PostByClientAsync<T>(HttpClient webClient, string url, object obj)
        {
            var request = new HttpRequestMessage(HttpMethod.Post, url);
            request.Content = new ByteArrayContent(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(obj)));
            request.Content.Headers.Add("Content-Type", "application/json");
            var response = await webClient.SendAsync(request);
            var ret = await response.Content.ReadAsStringAsync();
            return !string.IsNullOrEmpty(ret) ? ret.ToObjectFromJson<T>() : default(T);
        }

        public static async Task<HttpResponseMessage> PostByResponse(string url, object datajson)
        {
            var client = GetHttpClient();

            var request = new HttpRequestMessage(HttpMethod.Post, url);

            request.Content = new ByteArrayContent(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(datajson)));
            request.Content.Headers.Add("Content-Type", "application/json");

            var response = await client.SendAsync(request);
            return response;
        }

        public static async Task<string> PutByResponse(string url, object datajson)
        {
            var client = GetHttpClient();
            var request = new HttpRequestMessage(HttpMethod.Put, url);
            request.Content = new ByteArrayContent(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(datajson)));
            request.Content.Headers.Add("Content-Type", "application/json");

            var response = await client.SendAsync(request);
            var ret = await response.Content.ReadAsStringAsync();
            return ret;
        }

        public static async Task<string> PutByResponseByBasicAuth(string url, object datajson, string basicAuth)
        {
            var client = GetHttpClient();
            var request = new HttpRequestMessage(HttpMethod.Put, url);
            request.Content = new ByteArrayContent(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(datajson)));
            request.Content.Headers.Add("Content-Type", "application/json");
            if (!string.IsNullOrEmpty(basicAuth))
            {
                request.Headers.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes("elastic:lds123")));
            }

            var response = await client.SendAsync(request);
            var ret = await response.Content.ReadAsStringAsync();
            return ret;
        }

        private static string GetUrlParam(Dictionary<string, object> param)
        {
            StringBuilder buffer = new StringBuilder();
            int i = 0;
            if (param != null && param.Any())
            {
                foreach (var item in param)
                {
                    if (i > 0)
                    {
                        buffer.AppendFormat("&{0}={1}", item.Key, item.Value);
                    }
                    else
                    {
                        buffer.AppendFormat("{0}={1}", item.Key, item.Value);
                    }

                    i++;
                }
            }

            return buffer.ToString();
        }

        /// <summary>
        /// 获取的HttpClient可能10min后回收，所以请求超时时间不能超过10min
        /// </summary>
        /// <returns></returns>
        private static HttpClient GetHttpClient()
        {
            if (DateTime.Now < clientExpired)
            {
                return httpClientInstance;
            }

            lock (LockPad)
            {
                /// 避免网站IP地址变了，依旧访问旧的IP
                if (DateTime.Now >= clientExpired)
                {
                    var tmp = httpClientInstance;

                    Task.Run(async () =>
                    {
                        try
                        {
                            await Task.Delay(1000 * 60 * 11);

                            tmp?.Dispose();
                        }
                        catch
                        {
                        }
                    });

                    httpClientInstance = new HttpClient() { Timeout = TimeSpan.FromMinutes(10) };
                    clientExpired = DateTime.Now.AddMinutes(15);
                }
            }

            return httpClientInstance;
        }
    }
}